package top.guinguo.searcher_02.util;

import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.NumericField;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.Version;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by guin_guo on 2016/12/15.
 */
public class SearcherUtil {
    private String[] ids = {"1", "2", "3", "4", "5", "6"};
    private String[] emails = {"aa@ggn.org", "bb@ggn.com", "guinguo@sina.com", "five@g.com", "fsdf!@fdf.com", "ggn@ggn.org"};
    private String[] contents = {
            "welcome to visited the space, i like milk...",
            "i like say hello guinguo",
            "i like cyw is a sb and like bbs",
            "we are living in wushan badong like it",
            "i sushehao is 315 like michear",
            "i like play basketball and movie"};
    private int[] attachs = {1, 2, 4, 5, 6, 7};

    private Date[] dates;
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

    private String[] names = {"zhangsan", "lisi", "chengyiz", "chengguanw", "dezp", "guin_guo"};

    private Map<String, Float> score = new HashMap<String, Float>();
    private Directory directory;
    private IndexReader reader;

    public SearcherUtil() {
        directory = new RAMDirectory();
        setDates();
        index();
    }

    private void setDates() {
        try {
            dates = new Date[ids.length];
            for (int i = 0; i < ids.length; i++) {
                dates[i] = sdf.parse("2016-07-0"+(i+1));
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }


    public void index() {
        IndexWriter writer = null;
        try {
            writer = new IndexWriter(directory, new IndexWriterConfig(Version.LUCENE_35, new StandardAnalyzer(Version.LUCENE_35)));
            writer.deleteAll();
            Document doc = null;
            for (int i = 0; i < ids.length; i++) {
                doc = new Document();
                doc.add(new Field("id", ids[i], Field.Store.YES,Field.Index.NOT_ANALYZED));
                doc.add(new Field("email",emails[i], Field.Store.YES, Field.Index.NOT_ANALYZED));
                doc.add(new Field("content", contents[i], Field.Store.NO, Field.Index.ANALYZED));
                doc.add(new Field("name",names[i], Field.Store.YES, Field.Index.NOT_ANALYZED));
                doc.add(new NumericField("attach",Field.Store.YES,true).setIntValue(attachs[i]));
                doc.add(new NumericField("date", Field.Store.YES, true).setLongValue(dates[i].getTime()));
                //doc.add(new Field("attach", attachs[i], Field.Store.YES, Field.Index.NOT_ANALYZED));
                String et = emails[i].substring(emails[i].lastIndexOf("@") + 1);
                if (score.containsKey(et)) {
                    doc.setBoost(score.get(et));
                } else {
                    doc.setBoost(0.5f);
                }
                writer.addDocument(doc);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public IndexSearcher getSearcher(){
        try {
            if (reader == null) {
                reader = IndexReader.open(directory);
            } else {
                IndexReader ir = IndexReader.openIfChanged(reader);
                if (ir != null) {
                    reader.close();
                    reader = ir;
                }
            }
            return new IndexSearcher(reader);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public IndexSearcher getSearcher(Directory directory){
        try {
            if (reader == null) {
                reader = IndexReader.open(directory);
            } else {
                IndexReader ir = IndexReader.openIfChanged(reader);
                if (ir != null) {
                    reader.close();
                    reader = ir;
                }
            }
            return new IndexSearcher(reader);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public void searchByTerm(String field, String name, int num) {
        try {
            IndexSearcher searcher = getSearcher();
            Query query = new TermQuery(new Term(field, name));
            TopDocs tds = searcher.search(query,num);
            System.out.println("一共查询了：" + tds.totalHits);
            for (ScoreDoc sd : tds.scoreDocs) {
                Document doc = searcher.doc(sd.doc);
                System.out.println(doc.get("id")+"------->"+
                        "("+sd.doc+"-"+doc.getBoost()+","+sd.score+")"
                        +doc.get("name")+"["+doc.get("email")+"]-->"+doc.get("id")+","
                        +doc.get("attach")+","+doc.get("date"));
            }
            searcher.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //基于字符串区间搜索
    public void searchByTermRange(String field, String start,String end, int num) {
        try {
            IndexSearcher searcher = getSearcher();
            Query query = new TermRangeQuery(field, start, end, true, true);
            TopDocs tds = searcher.search(query,num);
            System.out.println("一共查询了：" + tds.totalHits);
            for (ScoreDoc sd : tds.scoreDocs) {
                Document doc = searcher.doc(sd.doc);
                System.out.println(doc.get("id")+"------->"+
                        "("+sd.doc+"-"+doc.getBoost()+","+sd.score+")"
                        +doc.get("name")+"["+doc.get("email")+"]-->"+doc.get("id")+","
                        +doc.get("attach")+","+doc.get("date"));
            }
            searcher.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //基于数字搜索
    public void searchByNumeric(String field, int start,int end, int num) {
        try {
            IndexSearcher searcher = getSearcher();
            Query query = NumericRangeQuery.newIntRange(field, start, end, true, true);
            TopDocs tds = searcher.search(query,num);
            System.out.println("一共查询了：" + tds.totalHits);
            for (ScoreDoc sd : tds.scoreDocs) {
                Document doc = searcher.doc(sd.doc);
                System.out.println(doc.get("id")+"------->"+
                        "("+sd.doc+"-"+doc.getBoost()+","+sd.score+")"
                        +doc.get("name")+"["+doc.get("email")+"]-->"+doc.get("id")+","
                        +doc.get("attach")+","+doc.get("date"));
            }
            searcher.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //基于前缀搜索
    public void searchByPrefix(String field, String value, int num) {
        try {
            IndexSearcher searcher = getSearcher();
            Query query = new PrefixQuery(new Term(field,value));
            TopDocs tds = searcher.search(query,num);
            System.out.println("一共查询了：" + tds.totalHits);
            for (ScoreDoc sd : tds.scoreDocs) {
                Document doc = searcher.doc(sd.doc);
                System.out.println(doc.get("id")+"------->"+
                        "("+sd.doc+"-"+doc.getBoost()+","+sd.score+")"
                        +doc.get("name")+"["+doc.get("email")+"]-->"+doc.get("id")+","
                        +doc.get("attach")+","+doc.get("date"));
            }
            searcher.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //基于通配符搜索
    public void searchByWildcard(String field, String value, int num) {
        try {
            IndexSearcher searcher = getSearcher();
            //value 可以使用通配符，?和*,?表示匹配一个字符，*表示任意多个字符
            Query query = new WildcardQuery(new Term(field,value));
            TopDocs tds = searcher.search(query,num);
            System.out.println("一共查询了：" + tds.totalHits);
            for (ScoreDoc sd : tds.scoreDocs) {
                Document doc = searcher.doc(sd.doc);
                System.out.println(doc.get("id")+"------->"+
                        "("+sd.doc+"-"+doc.getBoost()+","+sd.score+")"
                        +doc.get("name")+"["+doc.get("email")+"]-->"+doc.get("id")+","
                        +doc.get("attach")+","+doc.get("date"));
            }
            searcher.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public void searchByBoolean(int num) {
        try {
            IndexSearcher searcher = getSearcher();
            //value 可以使用通配符，?和*,?表示匹配一个字符，*表示任意多个字符
            BooleanQuery query = new BooleanQuery();
            /**
             * BooleanQuery可以连接多个子查询
             * BooleanClause.Occur.MUST 表示必须出现
             * BooleanClause.Occur.SHUOLD 表示可以出现
             * BooleanClause.Occur.MUST_NOT 表示不能出现
             */
            query.add(new TermQuery(new Term("name", "guin_guo")), BooleanClause.Occur.MUST_NOT);
            query.add(new TermQuery(new Term("content", "like")), BooleanClause.Occur.SHOULD);
            TopDocs tds = searcher.search(query,num);
            System.out.println("一共查询了：" + tds.totalHits);
            for (ScoreDoc sd : tds.scoreDocs) {
                Document doc = searcher.doc(sd.doc);
                System.out.println(doc.get("id")+"------->"+
                        "("+sd.doc+"-"+doc.getBoost()+","+sd.score+")"
                        +doc.get("name")+"["+doc.get("email")+"]-->"+doc.get("id")+","
                        +doc.get("attach")+","+doc.get("date"));
            }
            searcher.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //基于短语搜索
    public void searchByPhrase(int num) {
        try {
            IndexSearcher searcher = getSearcher();
            //value 可以使用通配符，?和*,?表示匹配一个字符，*表示任意多个字符
            PhraseQuery query = new PhraseQuery();
            query.setSlop(1);
            query.add(new Term("content", "i"));
            query.add(new Term("content", "say"));
            TopDocs tds = searcher.search(query,num);
            System.out.println("一共查询了：" + tds.totalHits);
            for (ScoreDoc sd : tds.scoreDocs) {
                Document doc = searcher.doc(sd.doc);
                System.out.println(doc.get("id")+"------->"+
                        "("+sd.doc+"-"+doc.getBoost()+","+sd.score+")"
                        +doc.get("name")+"["+doc.get("email")+"]-->"+doc.get("id")+","
                        +doc.get("attach")+","+doc.get("date"));
            }
            searcher.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //基于模糊搜索
    public void searchByFuzzy(int num) {
        try {
            IndexSearcher searcher = getSearcher();
            FuzzyQuery query = new FuzzyQuery(new Term("name", "guin_guogg"),0.5f);
            TopDocs tds = searcher.search(query,num);
            System.out.println("一共查询了：" + tds.totalHits);
            for (ScoreDoc sd : tds.scoreDocs) {
                Document doc = searcher.doc(sd.doc);
                System.out.println(doc.get("id")+"------->"+
                        "("+sd.doc+"-"+doc.getBoost()+","+sd.score+")"
                        +doc.get("name")+"["+doc.get("email")+"]-->"+doc.get("id")+","
                        +doc.get("attach")+","+doc.get("date"));
            }
            searcher.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void searchByQueryParse(Query query, int num) {
        try {
            IndexSearcher searcher = getSearcher();
            TopDocs tds = searcher.search(query,num);
            System.out.println("一共查询了：" + tds.totalHits);
            for (ScoreDoc sd : tds.scoreDocs) {
                Document doc = searcher.doc(sd.doc);
                System.out.println(doc.get("id")+"------->"+
                        "("+sd.doc+"-"+doc.getBoost()+","+sd.score+")"
                        +doc.get("name")+"["+doc.get("email")+"]-->"+doc.get("id")+","
                        +doc.get("attach")+","+doc.get("date"));
            }
            searcher.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void searchPage(String query, int pageIndex, int pageSize) {
        try {
            Directory dir = FileIndexUtils.getDirectory();
            IndexSearcher searcher = getSearcher(dir);
            QueryParser parser = new QueryParser(Version.LUCENE_35, "content", new StandardAnalyzer(Version.LUCENE_35));
            Query q = parser.parse(query);
            TopDocs tds = searcher.search(q,500);
            ScoreDoc[] sds = tds.scoreDocs;
            int start = (pageIndex - 1) * pageSize;
            int end = start + pageSize;
            for (int i=start;i<end;i++) {
                Document doc = searcher.doc(sds[i].doc);
                System.out.println(sds[i].doc+": "+doc.get("path") + "-->" + doc.get("filename"));
            }
            System.out.println("一共查询了：" + tds.totalHits);
            searcher.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (org.apache.lucene.queryParser.ParseException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据页码和大小获取上一次的最后一个ScoreDoc
     */
    private ScoreDoc getLastScoreDoc(int pageIndex, int pageSize, Query query, IndexSearcher searcher) throws IOException {
        if (pageIndex == 1) {//如果是第一页就返回空
            return null;
        }
        int num = (pageIndex-1) * pageSize;//获取上一页的数量
        TopDocs tds = searcher.search(query, num);
        return tds.scoreDocs[num - 1];
    }

    public void searchPageByAfter(String query, int pageIndex, int pageSize) {
        try {
            Directory dir = FileIndexUtils.getDirectory();
            IndexSearcher searcher = getSearcher(dir);
            QueryParser parser = new QueryParser(Version.LUCENE_35, "content", new StandardAnalyzer(Version.LUCENE_35));
            Query q = parser.parse(query);
            //先获取上一页的最后一个元素
            ScoreDoc lastSd = getLastScoreDoc(pageIndex, pageSize, q, searcher);
            //通过最后一个元素搜索下页的pageSize个元素
            TopDocs tds = searcher.searchAfter(lastSd, q, pageSize);
            for (ScoreDoc sd : tds.scoreDocs) {
                Document doc = searcher.doc(sd.doc);
                System.out.println(sd.doc+": "+doc.get("path") + "-->" + doc.get("filename"));
            }
            System.out.println("一共查询了：" + tds.totalHits);
            searcher.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (org.apache.lucene.queryParser.ParseException e) {
            e.printStackTrace();
        }
    }
}
